﻿#ifndef YUTOPP_CLIENT_DETAIL_SYNC_PART_HPP
#define YUTOPP_CLIENT_DETAIL_SYNC_PART_HPP

#include "basic_api.hpp"
#include "../url_base64.hpp"
#include "../response.hpp"
#include "../error.hpp"

#include <string>
#include <utility>

#include <boost/range/adaptor/transformed.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>

//void request
#define REQUEST_FUNCTION_TEMPLATE(n) \
	template<typename T BOOST_PP_ENUM_TRAILING_PARAMS(n, typename P)>

#define REQUEST_FUNCTION_ARG( z, n, unused ) \
	BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(BOOST_PP_CAT(const P, n),BOOST_PP_CAT(& p, n))

#define REQUEST_FUNCTION( n, pre, type, sig ) \
	REQUEST_FUNCTION_TEMPLATE(n) \
	pre ## REQUEST_FUNCTION_ ## type ## _ARG_LIST(n,sig) \
	pre ## REQUEST_FUNCTION_ ## type ## _IMPL(n,sig) \
	REQUEST_FUNCTION_TEMPLATE(n) \
	pre ## REQUEST_FUNCTION_ ## type ## _ARG_LIST_WITH_ERROR(n,sig) \
	pre ## REQUEST_FUNCTION_ ## type ## _IMPL_WITH_ERROR(n,sig)

//requestを定義
#define SYNC_REQUEST_FUNCTION_COMMON_ARG_LIST( n, unused ) \
	string_type inline request( const binary_type& ext BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _) )

#define SYNC_REQUEST_FUNCTION_COMMON_IMPL( n, unused ) \
	{ \
		typedef typename boost::remove_cv<T>::type	helper_type; \
		typedef string_type* tag; \
		const translator::decoder_type d; \
		error::value e; \
		 \
		const string_type data = d( \
			url::decode( \
				static_cast<Derived&>(*this).template request_cushion<helper_type, error::value>( \
					ext, \
					helper_type()(tag() BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p) ), \
					e \
					) \
				) \
			); \
		if ( e ) \
			error::detail::throw_exception( e ); \
		 \
		return data; \
	}

#define SYNC_REQUEST_FUNCTION_COMMON_ARG_LIST_WITH_ERROR( n, unused ) \
	string_type inline request( const binary_type& ext BOOST_PP_COMMA_IF(n) BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _), error::value& error )

#define SYNC_REQUEST_FUNCTION_COMMON_IMPL_WITH_ERROR( n, unused ) \
	{ \
		typedef typename boost::remove_cv<T>::type	helper_type; \
		typedef string_type* tag; \
		const translator::decoder_type d; \
		return d( \
			url::decode( \
				static_cast<Derived&>(*this).template request_cushion<helper_type, error::value>( \
					ext, \
					helper_type()(tag() BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p) ), \
					error \
					) \
				) \
			); \
	}

#define SYNC_REQUEST_FUNCTION_COMMON( z, n, unused ) \
	REQUEST_FUNCTION( n, SYNC_, COMMON, _ )

#define DEFINE_SYNC_REQUEST_FUNCTION_COMMON( n ) \
	BOOST_PP_REPEAT( \
		BOOST_PP_ADD( n, 1 ), \
		SYNC_REQUEST_FUNCTION_COMMON, \
		_ \
	)


//request_[hogehoge]を定義
#define SYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST(n,sig) \
	container::basic_response<string_type> request_ ## sig ## ( BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _) )

#define SYNC_REQUEST_FUNCTION_CUSTOM_IMPL(n,sig) \
	{ \
		return sig ## _tree_helper( request<T>( BOOST_PP_STRINGIZE( sig ) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p) ) ); \
	}

#define SYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST_WITH_ERROR(n,sig) \
	container::basic_response<string_type> request_ ## sig ## ( BOOST_PP_REPEAT(n, REQUEST_FUNCTION_ARG, _) BOOST_PP_COMMA_IF(n) error::value& error )

#define SYNC_REQUEST_FUNCTION_CUSTOM_IMPL_WITH_ERROR(n,sig) \
	{ \
		return sig ## _tree_helper( request<T>( BOOST_PP_STRINGIZE( sig ) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,p), error ) ); \
	}

#define SYNC_REQUEST_FUNCTION_CUSTOM( z, n, sig ) \
	REQUEST_FUNCTION( n, SYNC_, CUSTOM, sig )

#define DEFINE_SYNC_REQUEST_FUNCTION_CUSTOM( sig, n ) \
	BOOST_PP_REPEAT( \
		BOOST_PP_ADD( 1, n ), \
		SYNC_REQUEST_FUNCTION_CUSTOM, \
		sig \
	)


namespace webapp
{
	namespace clients
	{
		namespace detail
		{
			//コールバックが必須
			template<typename Derived, typename Policy>
			class sync_part
			{
				typedef typename Policy::string_type		string_type;
				typedef std::string							binary_type;
				typedef typename Policy::translator_type	translator;

			public:
				DEFINE_SYNC_REQUEST_FUNCTION_COMMON( 8 )
				DEFINE_SYNC_REQUEST_FUNCTION_CUSTOM( xml, 8 )

				// -- エラーを受け取る
				string_type raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const string_type& body,
					error::value& error )
				{
					typedef request::content::url_encoded		ue;
					typedef ue::template traits<string_type>	traits;
					typedef typename traits::parameter_type		parameter_type;

					if ( !body.empty() ) {
						//内容を分解する
						const parameter_type parameter = util::parse_url_encoded_text( body );

						if ( !parameter.empty() ) {
							return raw_urlencoded_request( method, path, ext, parameter, error );

						} else {
							error = error::value( error::critical, "invalid parameter." );
							return string_type();
						}
					}
					return raw_urlencoded_request( method, path, ext, parameter_type(), error );
				}

				string_type raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const typename request::content::url_encoded::template traits<string_type>::parameter_type& parameter,
					error::value& error )
				{
					typedef request::content::url_encoded ue;
					const translator::decoder_type d;

					return d(
						url::decode( 
							static_cast<Derived&>(*this).template request_cushion<ue>(
								static_cast<Derived&>(*this).get_address( method, path, ext ),
								parameter,
								static_cast<Derived&>(*this).generate_body( parameter, ue() ),
								ue(),
								error
								)
							)
						);
				}
				/*string_type raw_form_data_request( const string_type& method, const string_type& name, const string_type& ext, const string_type& body, const string_type& boundary, error::value& error )
				{
					try {
						return raw_form_data_request( method, name, ext, body, boundary );
					}
					catch( const error::exception& e ) {
						error = e;
					}
					return "";
				}*/

				// -- エラーを例外で受け取る。
				string_type raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const string_type& body )
				{
					error::value e;
					const string_type data = raw_urlencoded_request( method, path, ext, body, e );
					if ( e )
						error::detail::throw_exception( e );

					return data;
				}

				string_type raw_urlencoded_request(
					const binary_type& method,
					const binary_type& path,
					const binary_type& ext,
					const typename request::content::url_encoded::template traits<string_type>::parameter_type& parameter )
				{
					error::value e;
					const string_type data = raw_urlencoded_request( method, path, ext, parameter, e );
					if ( e )
						error::detail::throw_exception( e );

					return data;
				}

				/*string_type raw_form_data_request( const string_type& method, const string_type& path, const string_type& ext, const string_type& body, const string_type& boundary )
				{
					const auto address = static_cast<Derived&>(*this).get_address( path, ext );	
					return static_cast<Derived&>(*this).send_request(
						method,
						std::get<1>( address ),
						static_cast<Derived&>(*this).make_auth_header( method, std::get<2>( address ), param_type() ),
						body,
						"multipart/form-data; boundary=" + boundary
						);
				}*/
			};

		} // - detail
	} // - clients
} // - webapp

#undef REQUEST_FUNCTION_TEMPLATE
#undef REQUEST_FUNCTION_ARG
#undef REQUEST_FUNCTION
#undef SYNC_REQUEST_FUNCTION_COMMON_ARG_LIST
#undef SYNC_REQUEST_FUNCTION_COMMON_IMPL
#undef SYNC_REQUEST_FUNCTION_COMMON_ARG_LIST_WITH_ERROR
#undef SYNC_REQUEST_FUNCTION_COMMON_IMPL_WITH_ERROR
#undef SYNC_REQUEST_FUNCTION_COMMON
#undef DEFINE_SYNC_REQUEST_FUNCTION_COMMON
#undef SYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST
#undef SYNC_REQUEST_FUNCTION_CUSTOM_IMPL
#undef SYNC_REQUEST_FUNCTION_CUSTOM_ARG_LIST_WITH_ERROR
#undef SYNC_REQUEST_FUNCTION_CUSTOM_IMPL_WITH_ERROR
#undef SYNC_REQUEST_FUNCTION_CUSTOM
#undef DEFINE_SYNC_REQUEST_FUNCTION_CUSTOM

#endif /*YUTOPP_CLIENT_DETAIL_SYNC_PART_HPP*/
